package com.unionpay.upyzt.net;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.unionpay.upyzt.exception.AuthenticationException;
import com.unionpay.upyzt.net.APIResource.RequestMethod;
import com.unionpay.upyzt.util.StringUtils;
import com.unionpay.upyzt.util.UpyztSignature;
import lombok.Getter;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Getter
public abstract class UpyztRequest {
    /**
     * The HTTP method for the request (GET, POST, DELETE or PUT).
     */
    protected RequestMethod method;

    /**
     * The URL for the request.
     */
    protected URL url;

    /**
     * The HTTP headers of the request
     */
    protected HttpHeaders headers;

    /**
     * The body of the request.
     */
    protected HttpContent content;

    /**
     * The value of the {@code Authorization} header.
     */
    protected String authorization;

    /**
     * The options of the request.
     */
    protected RequestOptions options;


    protected static String getRequestURIFromURL(URL url) {
        String path = url.getPath();
        String query = url.getQuery();
        return query == null ? path : path + "?" + query;
    }

    /**
     * 将请求体写如输出流
     *
     * @param stream 输出流
     * @throws IOException IO 异常
     */
    public abstract void writeOutputStream(OutputStream stream) throws IOException;

    protected static HttpHeaders buildHeaders(String authorization, RequestOptions options) {
        Map<String, List<String>> headerMap = new HashMap<>();

        // Accept
        headerMap.put("Accept", Collections.singletonList("application/json"));

        // Accept-Charset
        headerMap.put("Accept-Charset", Collections.singletonList(APIResource.CHARSET.name()));

        // Accept-Language
        headerMap.put("Accept-Language", Collections.singletonList(options.getAcceptLanguage()));

        // Authorization
        headerMap.put("Authorization", Collections.singletonList(authorization));

        return HttpHeaders.of(headerMap);
    }

    protected static String buildAuthorization(RequestMethod method, URL url, HttpContent content, RequestOptions options) throws AuthenticationException {
        checkRootMchIdAndSignPrivateKey(options);
        String rootMchId = options.getRootMchId();
        String signPrivateKey = options.getSignPrivateKey();
        String currentTimeStr = StringUtils.currentTimeStamp();
        String nonceStr = StringUtils.randomAlphanumeric(32);
        String signData = method.name() + "\n" + getRequestURIFromURL(url) + "\n" + currentTimeStr + "\n" + nonceStr + "\n";
        if (content != null) {
            signData += content.getContent();
        }
        String sign = UpyztSignature.sign(signData, signPrivateKey, APIResource.CHARSET.name());
        if (sign == null) {
            throw new AuthenticationException("未设置正确的 Upyzt.signPrivateKey，无法生成签名");
        }
        return "UNIONPAY1-SHA256-RSA root_mch_id=" + rootMchId +
                ",nonce_str=" + nonceStr +
                ",timestamp=" + currentTimeStr +
                ",signature=" + sign;
    }

    protected static void checkRootMchIdAndSignPrivateKey(RequestOptions options) throws AuthenticationException {
        String rootMchId = options.getRootMchId();
        if (rootMchId == null || rootMchId.isEmpty()) {
            throw new AuthenticationException("未设置 Upyzt.rootMchId，请从管理后台 -> 开发管理 -> 开发信息管理 -> 平台编号获取后填写");
        } else if (StringUtils.containsWhitespace(rootMchId)) {
            throw new AuthenticationException("未设置正确的 Upyzt.rootMchId，不能包含空格");
        }
        String signPrivateKey = options.getSignPrivateKey();
        if (signPrivateKey == null || signPrivateKey.isEmpty()) {
            throw new AuthenticationException("未设置 Upyzt.signPrivateKey");
        }
    }
}
